import xID from './xID.js';
import {replaceByObj,numFormat,upload} from "./xUtil.js";
import xSpin from "./xSpin.js";
import xHint from "./xHint.js";
import {getImageMatrix,drawRotate,toImageData,addMark,canvasToBlob} from "./canvasUtil.js";

const tmplBase =`
    <div  class="{{class}}" tabindex = 99> 
       <div class ="xhead">
             <div class="body">
                  <div class="canvas-panel">   
                     <canvas id="{{viewId}}" class="view-canvas" data-key="dragable">                      
                    </canvas>
                     
                     <div class="tracker"  data-key="dragable">
                       <div class ="tracker-angle1"></div>
                       <div class ="tracker-angle2"></div>
                       <div class ="tracker-angle3"></div>
                       <div class ="tracker-angle4"></div>
                       <div class ="tracker-center"></div>
                    </div>
                    <div class="view-title" ><div>图片预览</div><div>支持图片拖拽调整</div><div>支持方向键微调</div></div>                   
                  </div> 
                  <div class="clip">
                     <canvas id="{{clipId}}" class="clip-canvas"></canvas>
                     <div class="clip-title" >裁剪预览</div>
                  </div>                               
              </div>
              
             <div class="footer">
                   <i class="flat iconfont icon-upload-pic" title="选择图片"></i>
                   <i class="flat iconfont icon-original-size" title="实际大小"></i>
                   <i class="flat iconfont icon-zoom-in" title ="放大"></i>
                   <i class="flat iconfont icon-zoom-out" title ="缩小"></i>
                   <i class="flat iconfont icon-rotate-right" title ="右旋90度"></i>
                   <i class="flat iconfont icon-crop" title ="剪切上传"></i>
             </div>
         <input type="file" id ="{{inputId}}" tabindex="-1" style="display: none;" accept="image/*">     
       </div>       
    </div>
`;

export default class xCrop{
    $root;
    $head;
    $imgCvs;
    $input;
    $clipCvs;
    $tracker;
    $viewTitle;
    $clipTitle;
    vCfg;
    //过渡的 画布 ，旋转需要用到
    vInterimCvs;
    vImg;
    vClipMatrix;
    vMarkMatrix;
    vClickId = 'click.'+xID.getID();
    vState ={};
    vHasClip = false;
    /* s src d dest W width H height*/
    vImageParam ={
        rotate:0,
        rate:-1,
        real:false,
        sW:0,
        sH:0,
        dW:0,
        dH:0,
        divW:1,
        divH:1,
        cW:0,
        cH:0
    };

    vBindEvent;
    vValue ='';
    vTmpEvent ={};

    vIDs={
        viewId:xID.getID(),
        clipId:xID.getID(),
        inputId:xID.getID()
    };

    constructor(cfg){
        this.$root = $('#' + cfg.el);
        this.$root.html(replaceByObj(tmplBase,{class:cfg.style||'crop',...this.vIDs}));
        this.vCfg = cfg;
        this.init();
    };

    init(){
        this.$input = this.$root.find('#'+this.vIDs.inputId);
        this.$imgCvs = document.getElementById(this.vIDs.viewId);
        this.vInterimCvs = document.createElement('canvas');
        this.vImg = new Image();
        this.$clipCvs = document.getElementById(this.vIDs.clipId);
        this.$tracker = this.$root.find(".tracker");
        this.$head = this.$root.find(".xhead");
        this.$viewTitle = this.$root.find(".view-title");
        this.$clipTitle = this.$root.find(".clip-title")
        this.initEvent();
        this.initSize();
        this.centerTracker();
        this.setTitle();
        if(this.vCfg.mark)
            this.loadMarkMatrix();

        if(this.vCfg.readonly)
            this.setReadonly(true);
    }


    initEvent(){
        this.vImg.onload= this.evImageLoad.bind(this);
        this.$root.on('click','.icon-upload-pic',this.evOpen.bind(this));
        this.$root.on('click','.icon-zoom-in',this.evZoomIn.bind(this));
        this.$root.on('click','.icon-zoom-out',this.evZoomOut.bind(this));
        this.$root.on('click','.icon-original-size',this.evActual.bind(this));
        this.$root.on('click','.icon-rotate-right',this.evRotateRight.bind(this));
        this.$root.on('click','.icon-crop',this.evClip.bind(this));
        this.$input.on('change',this.evInputChange.bind(this));
        this.$root.on('mousedown','[data-key="dragable"]',this.evImgCvsMouseDown.bind(this));
        this.$root.on('mousemove','[data-key="dragable"]',this.evImgCvsMouseMove.bind(this));
        this.$root.on('mouseup','[data-key="dragable"]',this.evImgCvsMouseUp.bind(this));
        this.$root.on('click', this.evRootClick.bind(this));
        this.$root.on('keydown',this.evKeydown.bind(this));
        this.$root.find(">div").blur(this.evOff.bind(this));
    }

    initSize(){
        let viewSize  = (this.vCfg.viewSize||'600*400').split('*');
        let clipSize =  (this.vCfg.clipSize||'200').split('*');

        let viewWidth = viewSize[0];
        let viewHeight = viewSize[1]||viewSize[0];

        let clipWidth = clipSize[0];
        let clipHeight = clipSize[1]||clipSize[0];

        //
        this.$root.find(".canvas-panel").width(viewWidth);
        this.$root.find(".canvas-panel").height(viewHeight);
        this.$root.find(".clip-canvas").width(clipWidth);
        this.$root.find(".clip-canvas").height(clipHeight);
        this.$tracker.height(clipHeight);
        this.$tracker.width(clipWidth);
        this.$imgCvs.width = viewWidth;
        this.$imgCvs.height = viewHeight;
        this.vImageParam.divH = viewHeight;
        this.vImageParam.divW = viewWidth;
        //this.vImageParam.dW = this.vImageParam.divW;
        //this.vImageParam.dH = this.vImageParam.divH;
        this.vImageParam.cH = clipHeight;
        this.vImageParam.cW = clipWidth;
        this.$clipCvs.width = clipWidth;
        this.$clipCvs.height = clipHeight;

    }

    /*
    总共 4 个状态
    active  ,无 active
    valid,无valid ,invalid
   */
    renderStatus(action,status){
        if(action=='add'){
            this.$head.addClass(status);
        }else{
            this.$head.removeClass(status);
        }
    }

    loadMarkMatrix(){
        let img = new Image();
        let _this = this;
        img.onload = function(){
            let cvs = document.createElement('canvas');
            let ctx = cvs.getContext("2d");
            cvs.width = img.width;
            cvs.height = img.height;
            ctx.drawImage(img,0,0);
            _this.vMarkMatrix =
                getImageMatrix(ctx.getImageData(0,0,cvs.width,cvs.height));
            cvs.remove();
            img.remove();
        };
        img.src = this.vCfg.mark;
    }

    /*
    *  1：加载图片时候 要 占满div （大的 缩，小的扩）
       2：1：1 要居中
    * */
    calculateDest(){
        // 1:1
        if(this.vImageParam.real){
            this.vImageParam.rate = 1;
            this.vImageParam.dH = this.vInterimCvs.height;
            this.vImageParam.dW = this.vInterimCvs.width;
        }else {
            //重新计算 rate
            if(this.vImageParam.rate ==-1){
                this.vImageParam.oldRate = null;
                this.calculateRate();
            }


            //根据rate 计算 目标的 width 和 height
            this.vImageParam.dH = Math.round(this.vInterimCvs.height*this.vImageParam.rate);
            this.vImageParam.dW = Math.round(this.vInterimCvs.width*this.vImageParam.rate);
        }

    }

    changeState(state){
        let clipDiv = this.$root.find('.clip');
        //let state = item.state==3?'uploaded':'unupload';
        clipDiv.removeClass('uploaded');
        clipDiv.removeClass('unupload');
        clipDiv.addClass(state);
    }

    calculateRate(){
        //计算rate
        let _h_rate = (this.vImageParam.divH / this.vInterimCvs.height).toFixed(2)-0 ,
            _w_rate = (this.vImageParam.divW / this.vInterimCvs.width).toFixed(2)-0 ;
        this.vImageParam.rate = Math.min(Math.min(_h_rate,_w_rate),1);
    }

    centerCanvas(){
        let top = Math.round((this.vImageParam.divH-this.vImageParam.dH)/2);
        let left = Math.round((this.vImageParam.divW-this.vImageParam.dW)/2);
        this.$imgCvs.style.top = top+"px";
        this.$imgCvs.style.left = left+"px";
    }

    centerTracker(){
        let top = Math.round((this.vImageParam.divH-this.vImageParam.cH)/2);
        let left = Math.round((this.vImageParam.divW-this.vImageParam.cW)/2);
        this.$tracker.css('top' ,top+"px");
        this.$tracker.css('left' ,left+"px");
    }

    setTitle(){
        if(this.vCfg.placeholder)
            this.$viewTitle.html(this.vCfg.placeholder);
        if(this.vCfg.clipTitle)
            this.$clipTitle.html(this.vCfg.clipTitle);
    }

    resetImageParam(){
        this.vImageParam.rotate =0;
        this.vImageParam.real =false;
        this.vImageParam.rate = -1;
    }

    imgCvsMove(deltaX,deltaY){
        this.$imgCvs.style.top = (deltaY+ parseInt(this.$imgCvs.style.top))+'px';
        this.$imgCvs.style.left = (deltaX+ parseInt(this.$imgCvs.style.left))+'px';
        this.vState.mouseX = this.vState.mouseX+deltaX;
        this.vState.mouseY = this.vState.mouseY+deltaY;
    }


    draw(isNew){
        xSpin.pop(this.$tracker[0]);
        drawRotate(this.vInterimCvs,this.vImg,this.vImageParam.rotate*90);
        this.calculateDest();
        this.drawCanvas();
        // 图片缩放  中心点 相对位置不变
        if(this.vImageParam.oldRate){
            let deltRate = this.vImageParam.oldRate-this.vImageParam.rate;
            let xSpan = this.vImageParam.divW/2 - parseInt(this.$imgCvs.style.left);
            let ySpan = this.vImageParam.divH/2 - parseInt(this.$imgCvs.style.top);
            let posXRate  = xSpan/(this.vImageParam.oldRate*this.vImageParam.sW);
            let posYRate  = ySpan/(this.vImageParam.oldRate*this.vImageParam.sH);
            posXRate = (posXRate < 0) ? 0:(posXRate>1 )?1:posXRate;
            posYRate = (posYRate < 0 )? 0:(posYRate>1 )?1:posYRate;
            this.imgCvsMove(this.vImageParam.sW*deltRate*posXRate,
                this.vImageParam.sH*deltRate*posYRate);
            this.vImageParam.oldRate = null;
        }

        if(isNew)
            this.centerCanvas();
        if(this.vImg.src)
            this.drawClip();
        xSpin.close();
    }

    drawCanvas(){
        // 设置目标画布的尺寸
        this.$imgCvs.width = this.vImageParam.dW;
        this.$imgCvs.height = this.vImageParam.dH;
        let ctx = this.$imgCvs.getContext("2d");
        ctx.clearRect(0,0,this.vImageParam.dW,this.vImageParam.dH);
        ctx.save();
        ctx.drawImage(this.vInterimCvs,0,0,this.vInterimCvs.width,this.vInterimCvs.height,
            0,0,this.vImageParam.dW,this.vImageParam.dH);
        ctx.restore();

        xHint.pop(numFormat(this.vImageParam.rate,'0%'),this.$tracker[0]);
    };

    drawClip(){
        this.$clipTitle.hide();
        let ctx = this.$clipCvs.getContext('2d');
        let y = parseInt(this.$tracker.css('top')) - parseInt($(this.$imgCvs).css('top'));
        let x = parseInt(this.$tracker.css('left')) - parseInt($(this.$imgCvs).css('left'));
        this.$clipCvs.width = this.vImageParam.cW;
        this.$clipCvs.height = this.vImageParam.cH;
        ctx.drawImage(this.$imgCvs,x,y,this.vImageParam.cW,this.vImageParam.cH,
            0,0,this.vImageParam.cW,this.vImageParam.cH);


        if(this.vMarkMatrix){
            let ctx =this.$clipCvs.getContext("2d");
            let imageData = ctx.getImageData(0,0,this.$clipCvs.width,this.$clipCvs.height);
            this.vClipMatrix = getImageMatrix(imageData);
            //增加水印
            addMark(this.vClipMatrix,this.vMarkMatrix,this.vCfg.markPos);
            toImageData(imageData,this.vClipMatrix);
            ctx.putImageData(imageData,0,0);
        }
        this.changeState('unupload');
    }

    uploadClip(){
        let cfg ={ uploadUrl:this.vCfg.uploadUrl||''};
        cfg.file = canvasToBlob(this.$clipCvs);
        cfg.readystatechange = this.evReadystatechange.bind(this);
        cfg.error = this.evUploadError.bind(this);
        cfg.timeout = this.evUploadTimeout.bind(this);
        upload(cfg);
    }

    verify(){
        let result = true;
        if(this.vCfg.mandatory ){
            if(!this.vValue){
                this.renderStatus("remove",'valid');
                this.renderStatus("add","invalid");
                result =false
            }else {
                this.renderStatus("remove",'invalid');
                this.renderStatus("add","valid");
            }
        }
        return result;
    }

    evKeydown(e){

        let keynum = window.event ? e.keyCode : e.which;
        if(this.vImg.src){
            switch(keynum){
                //left
                case 37:
                    this.imgCvsMove(-1,0);
                    this.drawClip();
                    e.stopImmediatePropagation();
                    e.preventDefault();
                    break;
                //up
                case 38:
                    this.imgCvsMove(0,-1);
                    this.drawClip();
                    e.stopImmediatePropagation();
                    e.preventDefault();
                    break;
                // right
                case 39:
                    this.imgCvsMove(1,0);
                    this.drawClip();
                    e.stopImmediatePropagation();
                    e.preventDefault();
                    break;
                    //down
                case 40:
                    this.imgCvsMove(0,1);
                    this.drawClip();
                    e.stopImmediatePropagation();
                    e.preventDefault();
                    break;
            }
        }

    }

    evRootClick(e){
        this.vTmpEvent.timeStamp = e.timeStamp;
        this.vTmpEvent.target =e .target;
        this.renderStatus("add","active");
        $(document).on(this.vClickId,this.evOff.bind(this));
    };

    evOff(e){
        if((e.type=='click' && (this.vTmpEvent.timeStamp !=e.timeStamp || this.vTmpEvent.target !=e.target))||
            (e.type=="blur" && (e.currentTarget != document.activeElement))){
            this.renderStatus('remove','active');
            this.verify();
            $(document).unbind(this.vClickId);
        }
    }

    evReadystatechange(e,cfg){
        let xhr = e.target;
        if(xhr.readyState == XMLHttpRequest.DONE && xhr.status == 500){
            xHint.pop("网络错误,上传失败!","#"+this.vIDs.clipId)
        };

        if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
            let body = JSON.parse(xhr.responseText);
            let filename = body.filename||body.FileName|| body.file_name||body.fileName;
            let url = body.url||body.URL||(this.vCfg.path+'/'+filename) ;
            this.vValue = url;
            cfg.request = null;
            this.vHasClip = true;
            this.verify();
            if(this.vBindEvent)
                this.vBindEvent(this.vValue);
            xHint.pop("剪切上传成功","#"+this.vIDs.clipId)
            this.changeState("uploaded");
        }
    }

    evUploadError(e,cfg){
        xHint.pop("上传失败!","#"+this.vIDs.clipId);
    }

    evUploadTimeout(e,cfg){
        xHint.pop("上传超时!","#"+this.vIDs.clipId);
    }

    evOpen(e){
        if(this.vCfg.readonly)
            return;
        this.$input[0].click();
    }

    evZoomIn(e){
        if(this.vImg.src){
            this.vImageParam.oldRate = this.vImageParam.rate;
            this.vImageParam.rate = Math.min(this.vImageParam.rate.toFixed(1)-0+0.1,20);
            this.vImageParam.real = false;
            this.draw();
        }
    }

    evZoomOut(e){
        if(this.vImg.src){
            this.vImageParam.oldRate = this.vImageParam.rate;
            this.vImageParam.rate = Math.max(this.vImageParam.rate.toFixed(1)-0.1,0.05);
            this.vImageParam.real = false;
            this.draw();
        }

    }

    evRotateRight(e){
        if(this.vImg.src){
            this.vImageParam.rotate  = (this.vImageParam.rotate+1)%4;
            this.vImageParam.rate = -1;
            this.draw(true);
        }
    }

    evClip(e){
        if(this.vCfg.readonly)
            return;
        if(this.vImg.src)
            this.uploadClip();
    }

    evActual(e){
        if(this.vImg.src){
            this.vImageParam.real = true;
            this.draw(true);
        }
    }

    evImageLoad(e){
        this.$viewTitle.hide();
        this.vImageParam.sW= this.vImg.width;
        this.vImageParam.sH = this.vImg.height;
        this.resetImageParam();
        //
        this.draw(true);
        //this.centerCanvas();
        this.changeState('unupload');
    }

    evImgCvsMouseDown(e){
        e.stopImmediatePropagation();
        if(this.vImg.src){
            this.vState.dragable = true;
            this.vState.mouseX = e.clientX;
            this.vState.mouseY = e.clientY;
        }
    }

    evImgCvsMouseMove(e)
    {
        e.stopImmediatePropagation();
        if (this.vState.dragable)
        {
            let deltaX = e.clientX - this.vState.mouseX;
            let deltaY = e.clientY - this.vState.mouseY;
            /*this.$imgCvs.style.top = (y+ parseInt(this.$imgCvs.style.top))+'px';
            this.$imgCvs.style.left = (x+ parseInt(this.$imgCvs.style.left))+'px';
            this.vState.mouseX = e.clientX;
            this.vState.mouseY = e.clientY;*/
            this.imgCvsMove(deltaX,deltaY);
        }
    }

    evImgCvsMouseUp(e)
    {
        e.stopImmediatePropagation();
        if(this.vImg.src){
            this.drawClip();
            this.vState.dragable = false;
        }

    }

    evInputChange(e){
        let _this = this;
        if(this.$input[0].files[0]){
            xSpin.pop(this.$tracker[0]);
            let reader =  new FileReader();
            reader.onload = function(e){
                _this.vImg.src = e.target.result;
            }
            reader.readAsDataURL(this.$input[0].files[0]);
        }
    }

    clear(){
        /*
        this.vImg.src ='';
        let ctx = this.$imgCvs.getContext('2d');
        ctx.clearRect(0,0,this.$imgCvs.width,this.$imgCvs.height);
        ctx.save(); */

        let clipCtx = this.$clipCvs.getContext('2d');
        clipCtx.clearRect(0,0,this.$clipCvs.width,this.$clipCvs.height);
        clipCtx.save();
        this.changeState('');
        //this.$viewTitle.show();
        this.$clipTitle.show();
    }

    initValue(value){
        if(value){
            if(this.vValue != value){
                this.vValue = value;
                //this.clear();
                let img = new Image();
                let _this = this;
                img.onload = function(){
                    let ctx = _this.$clipCvs.getContext("2d");
                    ctx.drawImage(img,0,0);
                    _this.$clipTitle.hide();
                    img.remove();
                };
                img.onerror = function(){
                    _this.clear();
                };
                img.src = this.vValue;
            }
        }else{
            this.vValue = value;
            this.clear();
        }

    }

    setReadonly(status){
        this.vCfg.readonly = status;
        if(status){
            this.renderStatus("add","readonly");
            this.$input.attr("readonly","readonly")
        }else{
            this.renderStatus("remove","readonly");
            this.$input.removeAttr("readonly")
        }
    }

    setBindEvent(event){
        this.vBindEvent = event;
    };

    setValue(value){
        this.initValue(value);
    };

    getValue(){
        return this.vValue;
    }
}